From a7a785ef9f77dda91b7169f05d013defc244979e Mon Sep 17 00:00:00 2001 From: Yuri Ferretti Date: Sun, 15 Sep 2019 12:36:22 -0300 Subject: [PATCH] Add @dynamicMemberLookup to ObservableType and SharedSequenceConvertibleType --- CHANGELOG.md | 3 + Rx.xcodeproj/project.pbxproj | 47 ++- .../SharedSequence/SharedSequence.swift | 11 +- .../xcschemes/RxExample-iOSTests.xcscheme | 4 - RxSwift/Observable.swift | 1 - RxSwift/ObservableType.swift | 11 + .../Observable+DynamicMemberLookupTests.swift | 1 + Sources/AllTestz/main.swift | 23 ++ .../SharedSequence+OperatorTest.swift | 20 ++ .../Observable+DynamicMemberLookupTests.swift | 297 ++++++++++++++++++ 10 files changed, 409 insertions(+), 9 deletions(-) create mode 120000 Sources/AllTestz/Observable+DynamicMemberLookupTests.swift create mode 100644 Tests/RxSwiftTests/Observable+DynamicMemberLookupTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 26b01f0e1..5851ddf81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to this project will be documented in this file. --- +* Adds keyPath based `@dynamicMemberLookup` to `ObservableType` and `SharedSequenceConvertibleType` to allow +easier attribute mapping + ## [5.0.1](https://github.com/ReactiveX/RxSwift/releases/tag/5.0.1) * Reverts Carthage integration from using static to dynamic libraries. #1960 diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 3cc8746bb..cd65350d3 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -46,6 +46,9 @@ 54700CA11CE37E1900EF3A8F /* UINavigationItem+RxTests.swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54700C9E1CE37D1000EF3A8F /* UINavigationItem+RxTests.swift.swift */; }; 54D2138E1CE0824E0028D5B4 /* UINavigationItem+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D2138C1CE081890028D5B4 /* UINavigationItem+Rx.swift */; }; 601AE3DA1EE24E4F00617386 /* SwiftSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 601AE3D91EE24E4F00617386 /* SwiftSupport.swift */; }; + 60AFEDCA232DCCE3007BD1E3 /* Observable+DynamicMemberLookupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60AFEDC7232DCC96007BD1E3 /* Observable+DynamicMemberLookupTests.swift */; }; + 60AFEDCB232DCCE4007BD1E3 /* Observable+DynamicMemberLookupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60AFEDC7232DCC96007BD1E3 /* Observable+DynamicMemberLookupTests.swift */; }; + 60AFEDCC232DCCE5007BD1E3 /* Observable+DynamicMemberLookupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60AFEDC7232DCC96007BD1E3 /* Observable+DynamicMemberLookupTests.swift */; }; 6B9CA56B202A1F44002C2D11 /* KeyPathBinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9CA568202A1F43002C2D11 /* KeyPathBinder.swift */; }; 6B9CA56E202A206A002C2D11 /* KeyPathBinder+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9CA56D202A1F96002C2D11 /* KeyPathBinder+RxTests.swift */; }; 6B9CA56F202A206B002C2D11 /* KeyPathBinder+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9CA56D202A1F96002C2D11 /* KeyPathBinder+RxTests.swift */; }; @@ -959,6 +962,7 @@ 54700C9E1CE37D1000EF3A8F /* UINavigationItem+RxTests.swift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+RxTests.swift.swift"; sourceTree = ""; }; 54D2138C1CE081890028D5B4 /* UINavigationItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+Rx.swift"; sourceTree = ""; }; 601AE3D91EE24E4F00617386 /* SwiftSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftSupport.swift; sourceTree = ""; }; + 60AFEDC7232DCC96007BD1E3 /* Observable+DynamicMemberLookupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Observable+DynamicMemberLookupTests.swift"; sourceTree = ""; }; 6B9CA568202A1F43002C2D11 /* KeyPathBinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPathBinder.swift; sourceTree = ""; }; 6B9CA56D202A1F96002C2D11 /* KeyPathBinder+RxTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KeyPathBinder+RxTests.swift"; sourceTree = ""; }; 7EDBAEAB1C89B1A5006CBE67 /* UITabBarItem+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITabBarItem+RxTests.swift"; sourceTree = ""; }; @@ -1262,7 +1266,7 @@ C85B01681DB2ACAF006043C3 /* Platform.Linux.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Platform.Linux.swift; sourceTree = ""; }; C85B01721DB2ACF2006043C3 /* Platform.Darwin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Platform.Darwin.swift; sourceTree = ""; }; C85B01731DB2ACF2006043C3 /* Platform.Linux.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Platform.Linux.swift; sourceTree = ""; }; - C85BA04B1C3878740075D68E /* PerformanceTests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; name = PerformanceTests.app; path = Microoptimizations.app; sourceTree = BUILT_PRODUCTS_DIR; }; + C85BA04B1C3878740075D68E /* Microoptimizations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Microoptimizations.app; sourceTree = BUILT_PRODUCTS_DIR; }; C85E6FBB1F52FF4F00C5681E /* Signal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signal.swift; sourceTree = ""; }; C85E6FBD1F53025700C5681E /* SchedulerType+SharedSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SchedulerType+SharedSequence.swift"; sourceTree = ""; }; C86781471DB8119900B2029A /* Bag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = ""; }; @@ -1974,6 +1978,7 @@ C820A9D11EB50B0900D431BC /* Observable+GroupByTests.swift */, C820A9691EB4F64800D431BC /* Observable+JustTests.swift */, C820A9B51EB5081400D431BC /* Observable+MapTests.swift */, + 60AFEDC7232DCC96007BD1E3 /* Observable+DynamicMemberLookupTests.swift */, C820A9F91EB510D500D431BC /* Observable+MaterializeTests.swift */, C820A9991EB5001C00D431BC /* Observable+MergeTests.swift */, C820A9551EB4ED7C00D431BC /* Observable+MulticastTests.swift */, @@ -2397,7 +2402,7 @@ C83508C31C386F6F0027C24C /* AllTests-iOS.xctest */, C83509841C38740E0027C24C /* AllTests-tvOS.xctest */, C83509941C38742C0027C24C /* AllTests-macOS.xctest */, - C85BA04B1C3878740075D68E /* PerformanceTests.app */, + C85BA04B1C3878740075D68E /* Microoptimizations.app */, C8E8BA551E2C181A00A4AC2C /* Benchmarks.xctest */, A2897D53225CA1E7004EA481 /* RxRelay.framework */, ); @@ -2643,7 +2648,7 @@ ); name = Microoptimizations; productName = PerformanceTests; - productReference = C85BA04B1C3878740075D68E /* PerformanceTests.app */; + productReference = C85BA04B1C3878740075D68E /* Microoptimizations.app */; productType = "com.apple.product-type.application"; }; C88FA4FD1C25C44800CCFEA4 /* RxTest */ = { @@ -3135,6 +3140,7 @@ C820A98A1EB4FBD600D431BC /* Observable+CatchTests.swift in Sources */, C835093A1C38706E0027C24C /* RuntimeStateSnapshot.swift in Sources */, C8561B661DFE1169005E97F1 /* ExampleTests.swift in Sources */, + 60AFEDCA232DCCE3007BD1E3 /* Observable+DynamicMemberLookupTests.swift in Sources */, C86B1E221D42BF5200130546 /* SchedulerTests.swift in Sources */, C820A9D61EB50C5C00D431BC /* Observable+DistinctUntilChangedTests.swift in Sources */, C820A9FE1EB5110E00D431BC /* Observable+DematerializeTests.swift in Sources */, @@ -3275,6 +3281,7 @@ C8C4F1881DE9DF0200003FA7 /* UITableView+RxTests.swift in Sources */, C820A9FF1EB5110E00D431BC /* Observable+DematerializeTests.swift in Sources */, 1E9DA0C622006858000EB80A /* Synchronized.swift in Sources */, + 60AFEDCB232DCCE4007BD1E3 /* Observable+DynamicMemberLookupTests.swift in Sources */, C83509EE1C3875580027C24C /* Observable.Extensions.swift in Sources */, C83509BD1C38750D0027C24C /* ControlPropertyTests.swift in Sources */, 4C5213AF225E22500079FC77 /* Observable+CompactMapTests.swift in Sources */, @@ -3544,6 +3551,7 @@ C83509DF1C38754F0027C24C /* TestVirtualScheduler.swift in Sources */, C820A9DC1EB50CAA00D431BC /* Observable+DoOnTests.swift in Sources */, C83509CD1C3875230027C24C /* NotificationCenterTests.swift in Sources */, + 60AFEDCC232DCCE5007BD1E3 /* Observable+DynamicMemberLookupTests.swift in Sources */, C83509DB1C38754C0027C24C /* EquatableArray.swift in Sources */, C820A9BC1EB5097700D431BC /* Observable+TakeTests.swift in Sources */, C8350A031C38755E0027C24C /* BehaviorSubjectTest.swift in Sources */, @@ -4031,7 +4039,9 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", @@ -4052,6 +4062,8 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -4069,6 +4081,8 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -4085,6 +4099,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; @@ -4108,6 +4123,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Tests/Info.plist; @@ -4127,6 +4143,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Tests/Info.plist; @@ -4148,6 +4165,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -4169,6 +4187,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; @@ -4187,6 +4206,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; @@ -4204,8 +4224,10 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -4216,6 +4238,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.11; PRODUCT_BUNDLE_IDENTIFIER = io.rx.PerformanceTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; }; name = Debug; @@ -4224,13 +4247,16 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; INFOPLIST_FILE = Tests/Microoptimizations/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.11; PRODUCT_BUNDLE_IDENTIFIER = io.rx.PerformanceTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; }; name = Release; @@ -4239,13 +4265,16 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; INFOPLIST_FILE = Tests/Microoptimizations/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.11; PRODUCT_BUNDLE_IDENTIFIER = io.rx.PerformanceTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; }; name = "Release-Tests"; @@ -4319,7 +4348,9 @@ C8633A951B08FA5500375D60 /* Release-Tests */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "-"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -4536,7 +4567,9 @@ C8A56AEE1AD7424700B4673B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "-"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -4552,7 +4585,9 @@ C8A56AEF1AD7424700B4673B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "-"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -4570,7 +4605,9 @@ buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Manual; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; @@ -4594,7 +4631,9 @@ buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Tests/Benchmarks/Info.plist; @@ -4612,7 +4651,9 @@ buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Tests/Benchmarks/Info.plist; diff --git a/RxCocoa/Traits/SharedSequence/SharedSequence.swift b/RxCocoa/Traits/SharedSequence/SharedSequence.swift index 22afcbd8b..16cf26b23 100644 --- a/RxCocoa/Traits/SharedSequence/SharedSequence.swift +++ b/RxCocoa/Traits/SharedSequence/SharedSequence.swift @@ -79,6 +79,7 @@ public protocol SharingStrategyProtocol { /** A type that can be converted to `SharedSequence`. */ +@dynamicMemberLookup public protocol SharedSequenceConvertibleType : ObservableConvertibleType { associatedtype SharingStrategy: SharingStrategyProtocol @@ -94,6 +95,15 @@ extension SharedSequenceConvertibleType { } } +/** + Allows easier mapping by exposing dynamic member keypaths + So Driver.just("string").map { $0.count } can rewritten as Driver.just("string").count + */ +extension SharedSequenceConvertibleType { + public subscript(dynamicMember keyPath: KeyPath) -> SharedSequence { + return self.map { $0[keyPath: keyPath]} + } +} extension SharedSequence { @@ -223,4 +233,3 @@ extension SharedSequence where Element: RxAbstractInteger { return SharedSequence(Observable.timer(dueTime, period: period, scheduler: SharingStrategy.scheduler)) } } - diff --git a/RxExample/RxExample.xcodeproj/xcshareddata/xcschemes/RxExample-iOSTests.xcscheme b/RxExample/RxExample.xcodeproj/xcshareddata/xcschemes/RxExample-iOSTests.xcscheme index ad51143d0..ed30e0f57 100644 --- a/RxExample/RxExample.xcodeproj/xcshareddata/xcschemes/RxExample-iOSTests.xcscheme +++ b/RxExample/RxExample.xcodeproj/xcshareddata/xcschemes/RxExample-iOSTests.xcscheme @@ -23,8 +23,6 @@ - - - - : ObservableType { #endif } } - diff --git a/RxSwift/ObservableType.swift b/RxSwift/ObservableType.swift index dea9bfc7d..1c7e144ce 100644 --- a/RxSwift/ObservableType.swift +++ b/RxSwift/ObservableType.swift @@ -7,6 +7,7 @@ // /// Represents a push style sequence. +@dynamicMemberLookup public protocol ObservableType: ObservableConvertibleType { /** Subscribes `observer` to receive events for this sequence. @@ -45,3 +46,13 @@ extension ObservableType { } } } + +/** +Allows easier mapping by exposing dynamic member keypaths +So Observable.just("string").map { $0.count } can rewritten as Observable.just("string").count +*/ +extension ObservableType { + public subscript(dynamicMember keyPath: KeyPath) -> Observable { + return self.map { $0[keyPath: keyPath]} + } +} diff --git a/Sources/AllTestz/Observable+DynamicMemberLookupTests.swift b/Sources/AllTestz/Observable+DynamicMemberLookupTests.swift new file mode 120000 index 000000000..6dda9053d --- /dev/null +++ b/Sources/AllTestz/Observable+DynamicMemberLookupTests.swift @@ -0,0 +1 @@ +../../Tests/RxSwiftTests/Observable+DynamicMemberLookupTests.swift \ No newline at end of file diff --git a/Sources/AllTestz/main.swift b/Sources/AllTestz/main.swift index 273fe4475..0ff10b798 100644 --- a/Sources/AllTestz/main.swift +++ b/Sources/AllTestz/main.swift @@ -760,6 +760,27 @@ final class ObservableDoOnTest_ : ObservableDoOnTest, RxTestCase { ] } } +final class ObservableDynamicMemberLookupTest_ : ObservableDynamicMemberLookupTest, RxTestCase { + #if os(macOS) + required override init() { + super.init() + } + #endif + + static var allTests: [(String, (ObservableDynamicMemberLookupTest_) -> () -> Void)] { return [ + ("testDynamicMap_Never", ObservableDynamicMemberLookupTest.testDynamicMap_Never), + ("testDynamicMap_Empty", ObservableDynamicMemberLookupTest.testDynamicMap_Empty), + ("testDynamicMap_Range", ObservableDynamicMemberLookupTest.testDynamicMap_Range), + ("testDynamicMap_Error", ObservableDynamicMemberLookupTest.testDynamicMap_Error), + ("testDynamicMap_Dispose", ObservableDynamicMemberLookupTest.testDynamicMap_Dispose), + ("testDynamicMapCompose_Never", ObservableDynamicMemberLookupTest.testDynamicMapCompose_Never), + ("testDynamicMapCompose_Empty", ObservableDynamicMemberLookupTest.testDynamicMapCompose_Empty), + ("testDynamicMapCompose_Range", ObservableDynamicMemberLookupTest.testDynamicMapCompose_Range), + ("testDynamicMapCompose_Error", ObservableDynamicMemberLookupTest.testDynamicMapCompose_Error), + ("testDynamicMapCompose_Dispose", ObservableDynamicMemberLookupTest.testDynamicMapCompose_Dispose), + ] } +} + final class ObservableElementAtTest_ : ObservableElementAtTest, RxTestCase { #if os(macOS) required override init() { @@ -1896,6 +1917,7 @@ final class SharedSequenceOperatorTests_ : SharedSequenceOperatorTests, RxTestCa ("testDriverFromOptionalWhenNil", SharedSequenceOperatorTests.testDriverFromOptionalWhenNil), ("testDriverFromSequence", SharedSequenceOperatorTests.testDriverFromSequence), ("testDriverFromArray", SharedSequenceOperatorTests.testDriverFromArray), + ("testAsDriver_dynamicMap", SharedSequenceOperatorTests.testAsDriver_dynamicMap), ] } } @@ -2085,6 +2107,7 @@ func XCTMain(_ tests: [() -> Void]) { testCase(ObservableDematerializeTest_.allTests), testCase(ObservableDistinctUntilChangedTest_.allTests), testCase(ObservableDoOnTest_.allTests), + testCase(ObservableDynamicMemberLookupTest_.allTests), testCase(ObservableElementAtTest_.allTests), testCase(ObservableEnumeratedTest_.allTests), testCase(ObservableFilterTest_.allTests), diff --git a/Tests/RxCocoaTests/SharedSequence+OperatorTest.swift b/Tests/RxCocoaTests/SharedSequence+OperatorTest.swift index 72f7af7eb..27709f9b4 100644 --- a/Tests/RxCocoaTests/SharedSequence+OperatorTest.swift +++ b/Tests/RxCocoaTests/SharedSequence+OperatorTest.swift @@ -1131,3 +1131,23 @@ extension SharedSequenceOperatorTests { } } } + +extension SharedSequenceOperatorTests { + func testAsDriver_dynamicMap() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: "").count + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.next("test")) + hotObservable.on(.next("testa")) + hotObservable.on(.next("testab")) + hotObservable.on(.error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [4, 5, 6, 0]) + } +} diff --git a/Tests/RxSwiftTests/Observable+DynamicMemberLookupTests.swift b/Tests/RxSwiftTests/Observable+DynamicMemberLookupTests.swift new file mode 100644 index 000000000..6ca125d3d --- /dev/null +++ b/Tests/RxSwiftTests/Observable+DynamicMemberLookupTests.swift @@ -0,0 +1,297 @@ +// +// Observable+DynamicMemberLookupTests.swift +// Tests +// +// Created by Yuri Ferretti on 14/09/19. +// Copyright © 2019 Krunoslav Zaher. All rights reserved. +// + +import XCTest +import RxSwift +import RxTest + +class ObservableDynamicMemberLookupTest : RxTest { +} + +extension ObservableDynamicMemberLookupTest { + func testDynamicMap_Never() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + .next(150, "test"), + ]) + + let res = scheduler.start { xs.count } + + let correctMessages: [Recorded>] = [ + ] + + let correctSubscriptions = [ + Subscription(200, 1000) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testDynamicMap_Empty() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + .next(150, "test"), + .completed(300) + ]) + + let res = scheduler.start { xs.count } + + let correctMessages = [ + Recorded.completed(300, Int.self) + ] + + let correctSubscriptions = [ + Subscription(200, 300) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testDynamicMap_Range() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + .next(150, "test"), + .next(210, "testa"), + .next(220, "testab"), + .next(230, "testabc"), + .next(240, "testabcd"), + .completed(300) + ]) + + let res = scheduler.start { xs.count } + + let correctMessages = Recorded.events( + .next(210, 5), + .next(220, 6), + .next(230, 7), + .next(240, 8), + .completed(300) + ) + + let correctSubscriptions = [ + Subscription(200, 300) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testDynamicMap_Error() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + .next(150, "test"), + .next(210, "testa"), + .next(220, "testab"), + .next(230, "testabc"), + .next(240, "testabcd"), + .error(300, testError) + ]) + + let res = scheduler.start { xs.count } + + let correctMessages = Recorded.events( + .next(210, 5), + .next(220, 6), + .next(230, 7), + .next(240, 8), + .error(300, testError) + ) + + let correctSubscriptions = [ + Subscription(200, 300) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testDynamicMap_Dispose() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + .next(150, "test"), + .next(210, "testa"), + .next(220, "testab"), + .next(230, "testabc"), + .next(240, "testabcd"), + .error(300, testError) + ]) + + let res = scheduler.start(disposed: 290) { xs.count } + + let correctMessages = Recorded.events( + .next(210, 5), + .next(220, 6), + .next(230, 7), + .next(240, 8) + ) + + let correctSubscriptions = [ + Subscription(200, 290) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + #if TRACE_RESOURCES + func testDynamicMapReleasesResourcesOnComplete() { + _ = Observable.just("test").count.subscribe() + } + + func testDynamicMap1ReleasesResourcesOnError() { + _ = Observable.error(testError).count.subscribe() + } + #endif +} + +// MARK: map compose +extension ObservableDynamicMemberLookupTest { + + private struct TestData { + let string: String + } + + func testDynamicMapCompose_Never() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + .next(150, TestData(string: "test")), + ]) + + let res = scheduler.start { xs.string.count } + + let correctMessages: [Recorded>] = [ + ] + + let correctSubscriptions = [ + Subscription(200, 1000) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testDynamicMapCompose_Empty() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + .next(150, TestData(string: "test")), + .completed(300) + ]) + + let res = scheduler.start { xs.string.count } + + let correctMessages = [ + Recorded.completed(300, Int.self) + ] + + let correctSubscriptions = [ + Subscription(200, 300) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testDynamicMapCompose_Range() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + .next(150, TestData(string: "test")), + .next(210, TestData(string: "testa")), + .next(220, TestData(string: "testab")), + .next(230, TestData(string: "testabc")), + .next(240, TestData(string: "testabcd")), + .completed(300) + ]) + + let res = scheduler.start { xs.string.count } + + let correctMessages = Recorded.events( + .next(210, 5), + .next(220, 6), + .next(230, 7), + .next(240, 8), + .completed(300) + ) + + let correctSubscriptions = [ + Subscription(200, 300) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testDynamicMapCompose_Error() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + .next(150, TestData(string: "test")), + .next(210, TestData(string: "testa")), + .next(220, TestData(string: "testab")), + .next(230, TestData(string: "testabc")), + .next(240, TestData(string: "testabcd")), + .error(300, testError) + ]) + + let res = scheduler.start { xs.string.count } + + let correctMessages = Recorded.events( + .next(210, 5), + .next(220, 6), + .next(230, 7), + .next(240, 8), + .error(300, testError) + ) + + let correctSubscriptions = [ + Subscription(200, 300) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testDynamicMapCompose_Dispose() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + .next(150, TestData(string: "test")), + .next(210, TestData(string: "testa")), + .next(220, TestData(string: "testab")), + .next(230, TestData(string: "testabc")), + .next(240, TestData(string: "testabcd")), + .error(300, testError) + ]) + + let res = scheduler.start(disposed: 290) { xs.string.count } + + let correctMessages = Recorded.events( + .next(210, 5), + .next(220, 6), + .next(230, 7), + .next(240, 8) + ) + + let correctSubscriptions = [ + Subscription(200, 290) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } +} +