From bc644b1b0dd8305d2e07ca594c30bfb7c516891d Mon Sep 17 00:00:00 2001 From: Jasdev Singh Date: Mon, 11 May 2020 15:27:59 -0400 Subject: [PATCH] Adds Optional.publisher (note casing) as a convenience for Optional.Publisher.init. --- CombineExt.xcodeproj/project.pbxproj | 16 ++++++++++ README.md | 25 ++++++++++++++++ Sources/Convenience/Optional.swift | 19 ++++++++++++ Tests/OptionalTests.swift | 44 ++++++++++++++++++++++++++++ Tests/PrefixDurationTests.swift | 4 +-- 5 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 Sources/Convenience/Optional.swift create mode 100644 Tests/OptionalTests.swift diff --git a/CombineExt.xcodeproj/project.pbxproj b/CombineExt.xcodeproj/project.pbxproj index f632aca..3096689 100644 --- a/CombineExt.xcodeproj/project.pbxproj +++ b/CombineExt.xcodeproj/project.pbxproj @@ -60,6 +60,8 @@ BF9D85D724450090001783E6 /* ShareReplayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9D85D624450090001783E6 /* ShareReplayTests.swift */; }; BFB4EA132428256B0096E9E9 /* CombineLatestMany.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB4EA122428256B0096E9E9 /* CombineLatestMany.swift */; }; BFB4EA1524283ECF0096E9E9 /* CombineLatestManyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB4EA1424283ECF0096E9E9 /* CombineLatestManyTests.swift */; }; + BFF0BFF12469D7D800399570 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0BFF02469D7D800399570 /* Optional.swift */; }; + BFF0BFF52469D92C00399570 /* OptionalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0BFF42469D92C00399570 /* OptionalTests.swift */; }; DC16910F24281A1800B234C4 /* MapMany.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC16910E24281A1800B234C4 /* MapMany.swift */; }; DC1691122428228200B234C4 /* MapManyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC1691112428228200B234C4 /* MapManyTests.swift */; }; OBJ_22 /* AssignToMany.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* AssignToMany.swift */; }; @@ -117,6 +119,8 @@ BF9D85D624450090001783E6 /* ShareReplayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareReplayTests.swift; sourceTree = ""; }; BFB4EA122428256B0096E9E9 /* CombineLatestMany.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineLatestMany.swift; sourceTree = ""; }; BFB4EA1424283ECF0096E9E9 /* CombineLatestManyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineLatestManyTests.swift; sourceTree = ""; }; + BFF0BFF02469D7D800399570 /* Optional.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = ""; }; + BFF0BFF42469D92C00399570 /* OptionalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalTests.swift; sourceTree = ""; }; "CombineExt::CombineExt::Product" /* CombineExt.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CombineExt.framework; sourceTree = BUILT_PRODUCTS_DIR; }; "CombineExt::CombineExtTests::Product" /* CombineExtTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = CombineExtTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DC16910E24281A1800B234C4 /* MapMany.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapMany.swift; sourceTree = ""; }; @@ -190,6 +194,14 @@ path = Subjects; sourceTree = ""; }; + BFF0BFEF2469D7A500399570 /* Convenience */ = { + isa = PBXGroup; + children = ( + BFF0BFF02469D7D800399570 /* Optional.swift */, + ); + path = Convenience; + sourceTree = ""; + }; OBJ_11 /* Tests */ = { isa = PBXGroup; children = ( @@ -212,6 +224,7 @@ BF9D85D42444D12F001783E6 /* ReplaySubjectTests.swift */, BF9D85D624450090001783E6 /* ShareReplayTests.swift */, BF8EDF4D2453540D00B0CC75 /* PrefixDurationTests.swift */, + BFF0BFF42469D92C00399570 /* OptionalTests.swift */, ); path = Tests; sourceTree = SOURCE_ROOT; @@ -240,6 +253,7 @@ OBJ_7 /* Sources */ = { isa = PBXGroup; children = ( + BFF0BFEF2469D7A500399570 /* Convenience */, BF9D85D12444BB7F001783E6 /* Subjects */, 78C193DA241D07160001B7FD /* Common */, 78C193D5241C2E4F0001B7FD /* Models */, @@ -378,6 +392,7 @@ files = ( BF3C6B6C24421D27004D4A8A /* ShareReplay.swift in Sources */, DC16910F24281A1800B234C4 /* MapMany.swift in Sources */, + BFF0BFF12469D7D800399570 /* Optional.swift in Sources */, 78C193CF241C16C40001B7FD /* FlatMapLatest.swift in Sources */, 78C193DC241D0A9F0001B7FD /* Sink.swift in Sources */, 788CD8FB2431228C0015B3C7 /* Amb.swift in Sources */, @@ -416,6 +431,7 @@ buildActionMask = 0; files = ( 78C193D2241C1B750001B7FD /* FlatMapLatestTests.swift in Sources */, + BFF0BFF52469D92C00399570 /* OptionalTests.swift in Sources */, 78AA9297241B8532009BD68B /* AssignToManyTests.swift in Sources */, BFB4EA1524283ECF0096E9E9 /* CombineLatestManyTests.swift in Sources */, 71E6F4EE2465616100FB4103 /* AssignOwnershipTests.swift in Sources */, diff --git a/README.md b/README.md index 4af4740..8796a17 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,9 @@ All operators, utilities and helpers respect Combine's publisher contract, inclu ### Subjects * [ReplaySubject](#ReplaySubject) +### Convenience +* [Optional.publisher](#Optionalpublisher) + > **Note**: This is still a relatively early version of CombineExt, with much more to be desired. I gladly accept PRs, ideas, opinions, or improvements. Thank you! :) ## Installation @@ -642,6 +645,28 @@ subject.send(5) 5 ``` +## Convenience + +## Optional.publisher + +`Optional.publisher` is a property version of [`Optional.Publisher.init`](https://developer.apple.com/documentation/swift/optional/publisher/3343960-init). It puts the type on equal footing with [`Result.publisher`](https://developer.apple.com/documentation/swift/result/3344716-publisher) and [`Sequence.publisher`](https://developer.apple.com/documentation/swift/sequence/3344717-publisher). + +So you can use: + +```swift +let number: Int? = 1 +number.publisher + /* … */ +``` + +Instead of: + +```swift +let number: Int? = 1 +Optional.Publisher(number) + /* … */ +``` + ## License MIT, of course ;-) See the [LICENSE](LICENSE) file. diff --git a/Sources/Convenience/Optional.swift b/Sources/Convenience/Optional.swift new file mode 100644 index 0000000..968c68b --- /dev/null +++ b/Sources/Convenience/Optional.swift @@ -0,0 +1,19 @@ +// +// Optional.swift +// CombineExt +// +// Created by Jasdev Singh on 11/05/2020. +// Copyright © 2020 Combine Community. All rights reserved. +// + +#if canImport(Combine) +import Combine + +@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) +public extension Optional { + /// A publisher that publishes an optional value to each subscriber exactly once, if the optional has a value. + var publisher: Optional.Publisher { + Optional.Publisher(self) + } +} +#endif diff --git a/Tests/OptionalTests.swift b/Tests/OptionalTests.swift new file mode 100644 index 0000000..be6c329 --- /dev/null +++ b/Tests/OptionalTests.swift @@ -0,0 +1,44 @@ +// +// OptionalTests.swift +// CombineExt +// +// Created by Jasdev Singh on 11/05/2020. +// Copyright © 2020 Combine Community. All rights reserved. +// + +#if !os(watchOS) +import Combine +import CombineExt +import XCTest + +@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) +final class OptionalTests: XCTestCase { + private var subscription: AnyCancellable! + + func testSomeInitialization() { + var results = [Int]() + var completion: Subscribers.Completion? + + subscription = Optional(1) + .publisher + .sink(receiveCompletion: { completion = $0 }, + receiveValue: { results.append($0) }) + + XCTAssertEqual([1], results) + XCTAssertEqual(.finished, completion) + } + + func testNoneInitialization() { + var results = [Int]() + var completion: Subscribers.Completion? + + subscription = Optional.none + .publisher + .sink(receiveCompletion: { completion = $0 }, + receiveValue: { results.append($0) }) + + XCTAssertTrue(results.isEmpty) + XCTAssertEqual(.finished, completion) + } +} +#endif diff --git a/Tests/PrefixDurationTests.swift b/Tests/PrefixDurationTests.swift index 9e1b0bd..2113730 100644 --- a/Tests/PrefixDurationTests.swift +++ b/Tests/PrefixDurationTests.swift @@ -47,7 +47,7 @@ final class PrefixDurationTests: XCTestCase { var completions = [Subscribers.Completion]() cancellable = subject - .prefix(duration: 0.5) + .prefix(duration: 0.8) .sink(receiveCompletion: { completions.append($0); expectation.fulfill() }, receiveValue: { results.append($0) }) @@ -61,7 +61,7 @@ final class PrefixDurationTests: XCTestCase { subject.send(3) } - DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { subject.send(4) subject.send(5) subject.send(completion: .finished)