Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions ReactiveSwift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
9A1D067D1D948A2300ACF44C /* UnidirectionalBindingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D067C1D948A2200ACF44C /* UnidirectionalBindingSpec.swift */; };
9A1D067E1D948A2300ACF44C /* UnidirectionalBindingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D067C1D948A2200ACF44C /* UnidirectionalBindingSpec.swift */; };
9A1D067F1D948A2300ACF44C /* UnidirectionalBindingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D067C1D948A2200ACF44C /* UnidirectionalBindingSpec.swift */; };
9A5D93731EE5733300438925 /* InhabitableTypeGuards.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5D93721EE5733300438925 /* InhabitableTypeGuards.swift */; };
9A5D93741EE5733300438925 /* InhabitableTypeGuards.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5D93721EE5733300438925 /* InhabitableTypeGuards.swift */; };
9A5D93751EE5733300438925 /* InhabitableTypeGuards.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5D93721EE5733300438925 /* InhabitableTypeGuards.swift */; };
9A5D93761EE5733300438925 /* InhabitableTypeGuards.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5D93721EE5733300438925 /* InhabitableTypeGuards.swift */; };
9A681A9E1E5A241B00B097CF /* DeprecationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A681A9D1E5A241B00B097CF /* DeprecationSpec.swift */; };
9A681A9F1E5A241B00B097CF /* DeprecationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A681A9D1E5A241B00B097CF /* DeprecationSpec.swift */; };
9A681AA01E5A241B00B097CF /* DeprecationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A681A9D1E5A241B00B097CF /* DeprecationSpec.swift */; };
Expand Down Expand Up @@ -245,6 +249,7 @@
9A090C131DA0309E00EE97CA /* Reactive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reactive.swift; sourceTree = "<group>"; };
9A1A4F981E16961C006F3039 /* ValidatingPropertySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidatingPropertySpec.swift; sourceTree = "<group>"; };
9A1D067C1D948A2200ACF44C /* UnidirectionalBindingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnidirectionalBindingSpec.swift; sourceTree = "<group>"; };
9A5D93721EE5733300438925 /* InhabitableTypeGuards.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InhabitableTypeGuards.swift; sourceTree = "<group>"; };
9A681A9D1E5A241B00B097CF /* DeprecationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeprecationSpec.swift; sourceTree = "<group>"; };
9A9100DE1E0E6E620093E346 /* ValidatingProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidatingProperty.swift; sourceTree = "<group>"; };
9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -412,6 +417,7 @@
D08C54B11A69A2AC00AD8286 /* Signal.swift */,
D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */,
BE9CF3941D751B6B003AE479 /* UnidirectionalBinding.swift */,
9A5D93721EE5733300438925 /* InhabitableTypeGuards.swift */,
);
name = Signals;
sourceTree = "<group>";
Expand Down Expand Up @@ -884,6 +890,7 @@
9A090C171DA0309E00EE97CA /* Reactive.swift in Sources */,
57A4D1BB1BA13D7A00F7D4B1 /* Signal.swift in Sources */,
57A4D1BC1BA13D7A00F7D4B1 /* SignalProducer.swift in Sources */,
9A5D93761EE5733300438925 /* InhabitableTypeGuards.swift in Sources */,
57A4D1BD1BA13D7A00F7D4B1 /* Atomic.swift in Sources */,
57A4D1BE1BA13D7A00F7D4B1 /* Bag.swift in Sources */,
57A4D1C01BA13D7A00F7D4B1 /* FoundationExtensions.swift in Sources */,
Expand Down Expand Up @@ -938,6 +945,7 @@
9A090C161DA0309E00EE97CA /* Reactive.swift in Sources */,
A9B315C31B3940810001CB9C /* Signal.swift in Sources */,
A9B315C41B3940810001CB9C /* SignalProducer.swift in Sources */,
9A5D93751EE5733300438925 /* InhabitableTypeGuards.swift in Sources */,
A9B315C51B3940810001CB9C /* Atomic.swift in Sources */,
A9B315C61B3940810001CB9C /* Bag.swift in Sources */,
A9B315C81B3940810001CB9C /* FoundationExtensions.swift in Sources */,
Expand Down Expand Up @@ -965,6 +973,7 @@
D08C54B31A69A2AE00AD8286 /* Signal.swift in Sources */,
D85C652A1C0D84C7005A77AD /* Flatten.swift in Sources */,
D0C312CF19EF2A5800984962 /* Bag.swift in Sources */,
9A5D93731EE5733300438925 /* InhabitableTypeGuards.swift in Sources */,
4A0E10FF1D2A92720065D310 /* Lifetime.swift in Sources */,
D0C312E719EF2A5800984962 /* Scheduler.swift in Sources */,
D0C312CD19EF2A5800984962 /* Atomic.swift in Sources */,
Expand Down Expand Up @@ -1019,6 +1028,7 @@
9A090C151DA0309E00EE97CA /* Reactive.swift in Sources */,
D85C652B1C0E70E3005A77AD /* Flatten.swift in Sources */,
4A0E11001D2A92720065D310 /* Lifetime.swift in Sources */,
9A5D93741EE5733300438925 /* InhabitableTypeGuards.swift in Sources */,
D08C54BB1A69C54400AD8286 /* Property.swift in Sources */,
D03B4A3E19F4C39A009E02AC /* FoundationExtensions.swift in Sources */,
D08C54B71A69A3DB00AD8286 /* Event.swift in Sources */,
Expand Down
127 changes: 127 additions & 0 deletions Sources/InhabitableTypeGuards.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import Result

// Observation
extension SignalProducer where Value == Never {
@discardableResult
@available(*, deprecated, message:"`success` event is never delivered.")
public func startWithResult(_ action: @escaping (Result<Value, Error>) -> Void) -> Disposable { fatalError() }

@discardableResult
@available(*, deprecated, message:"The observer is never invoked.")
public func startWithValues(_ action: @escaping (Value) -> Void) -> Disposable { fatalError() }
}

extension SignalProducer where Value == Never, Error == NoError {
@discardableResult
@available(*, deprecated, message:"The observer is never invoked.")
public func startWithResult(_ action: @escaping (Result<Value, Error>) -> Void) -> Disposable { fatalError() }
}

extension SignalProducer where Error == NoError {
@discardableResult
@available(*, deprecated, message:"The observer is never invoked.")
public func startWithFailed(_ action: @escaping (Error) -> Void) -> Disposable { fatalError() }
}

extension Signal where Value == Never {
@discardableResult
@available(*, deprecated, message:"`success` event is never delivered.")
public func observeResult(_ action: @escaping (Result<Value, Error>) -> Void) -> Disposable { fatalError() }

@discardableResult
@available(*, deprecated, message:"The observer is never invoked.")
public func observeValues(_ action: @escaping (Value) -> Void) -> Disposable { fatalError() }
}

extension Signal where Value == Never, Error == NoError {
@discardableResult
@available(*, deprecated, message:"The observer is never invoked.")
public func observeResult(_ action: @escaping (Result<Value, Error>) -> Void) -> Disposable { fatalError() }
}

extension Signal where Error == NoError {
@discardableResult
@available(*, deprecated, message:"The observer is never invoked.")
public func observeFailed(_ action: @escaping (Error) -> Void) -> Disposable { fatalError() }
}

// flatMap
extension SignalProducer where Value == Never {
@discardableResult
@available(*, deprecated, message:"Use `promoteValue` instead.")
public func flatMap<Inner: SignalProducerConvertible>(_ strategy: FlattenStrategy, _ transform: @escaping (Value) -> Inner) -> SignalProducer<Inner.Value, Error> where Inner.Error == Error { fatalError() }

@discardableResult
@available(*, deprecated, message:"Use `promoteValue` instead.")
public func flatMap<Inner: SignalProducerConvertible>(_ strategy: FlattenStrategy, _ transform: @escaping (Value) -> Inner) -> SignalProducer<Inner.Value, Error> where Inner.Error == NoError { fatalError() }
}

extension SignalProducer where Value == Never, Error == NoError {
@discardableResult
@available(*, deprecated, message:"Use `promoteValue` instead.")
public func flatMap<Inner: SignalProducerConvertible>(_ strategy: FlattenStrategy, _ transform: @escaping (Value) -> Inner) -> SignalProducer<Inner.Value, Inner.Error> { fatalError() }

@discardableResult
@available(*, deprecated, message:"Use `promoteValue` instead.")
public func flatMap<Inner: SignalProducerConvertible>(_ strategy: FlattenStrategy, _ transform: @escaping (Value) -> Inner) -> SignalProducer<Inner.Value, Inner.Error> where Inner.Error == Error { fatalError() }
}

extension SignalProducer where Error == NoError {
@discardableResult
@available(*, deprecated, message:"Use `promoteError` instead.")
public func flatMapError<NewError>(_ transform: @escaping (Error) -> SignalProducer<Value, NewError>) -> SignalProducer<Value, NewError> { fatalError() }
}

extension Signal where Value == Never {
@discardableResult
@available(*, deprecated, message:"Use `promoteValue` instead.")
public func flatMap<Inner: SignalProducerConvertible>(_ strategy: FlattenStrategy, _ transform: @escaping (Value) -> Inner) -> Signal<Inner.Value, Error> where Inner.Error == Error { fatalError() }

@discardableResult
@available(*, deprecated, message:"Use `promoteValue` instead.")
public func flatMap<Inner: SignalProducerConvertible>(_ strategy: FlattenStrategy, _ transform: @escaping (Value) -> Inner) -> Signal<Inner.Value, Error> where Inner.Error == NoError { fatalError() }

}

extension Signal where Value == Never, Error == NoError {
@discardableResult
@available(*, deprecated, message:"Use `promoteValue` instead.")
public func flatMap<Inner: SignalProducerConvertible>(_ strategy: FlattenStrategy, _ transform: @escaping (Value) -> Inner) -> Signal<Inner.Value, Inner.Error> { fatalError() }

@discardableResult
@available(*, deprecated, message:"Use `promoteValue` instead.")
public func flatMap<Inner: SignalProducerConvertible>(_ strategy: FlattenStrategy, _ transform: @escaping (Value) -> Inner) -> Signal<Inner.Value, Inner.Error> where Inner.Error == Error { fatalError() }
}

extension Signal where Error == NoError {
@discardableResult
@available(*, deprecated, message:"Use `promoteError` instead.")
public func flatMapError<NewError>(_ transform: @escaping (Error) -> SignalProducer<Value, NewError>) -> Signal<Value, NewError> { fatalError() }
}

/*
func test() {
SignalProducer<Never, AnyError>.never.startWithResult { _ in }
SignalProducer<Never, NoError>.never.startWithResult { _ in }
SignalProducer<Any, NoError>.never.startWithFailed { _ in }
SignalProducer<Never, NoError>.never.startWithFailed { _ in }
Signal<Never, AnyError>.never.observeResult { _ in }
Signal<Never, NoError>.never.observeResult { _ in }
Signal<Any, NoError>.never.observeFailed { _ in }
Signal<Never, NoError>.never.observeFailed { _ in }

SignalProducer<Never, AnyError>.never.flatMap(.latest) { _ in SignalProducer<Int, AnyError>.empty }
SignalProducer<Never, AnyError>.never.flatMap(.latest) { _ in SignalProducer<Int, NoError>.empty }
SignalProducer<Never, NoError>.never.flatMap(.latest) { _ in SignalProducer<Int, AnyError>.empty }
SignalProducer<Never, NoError>.never.flatMap(.latest) { _ in SignalProducer<Int, NoError>.empty }
SignalProducer<Never, NoError>.never.flatMapError { _ in SignalProducer<Never, AnyError>.empty }
SignalProducer<Never, NoError>.never.flatMapError { _ in SignalProducer<Never, NoError>.empty }

Signal<Never, AnyError>.never.flatMap(.latest) { _ in SignalProducer<Int, AnyError>.empty }
Signal<Never, AnyError>.never.flatMap(.latest) { _ in SignalProducer<Int, NoError>.empty }
Signal<Never, NoError>.never.flatMap(.latest) { _ in SignalProducer<Int, AnyError>.empty }
Signal<Never, NoError>.never.flatMap(.latest) { _ in SignalProducer<Int, NoError>.empty }
Signal<Never, NoError>.never.flatMapError { _ in SignalProducer<Never, AnyError>.empty }
Signal<Never, NoError>.never.flatMapError { _ in SignalProducer<Never, NoError>.empty }
}
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this?

Copy link
Member Author

@andersio andersio Jun 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a bunch of test cases to verify these combinations would emit the error message. Moving them to the spec would flood the IDE with warnings, so I just commented them out. 🤦‍♂️

41 changes: 41 additions & 0 deletions Sources/Signal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2473,6 +2473,47 @@ extension Signal where Error == NoError {
}
}

extension Signal where Value == Never {
/// Promote a signal that does not generate values, as indicated by `Never`, to be
/// a signal of the given type of value.
///
/// - note: The promotion does not result in any value being generated.
///
/// - parameters:
/// - _ The type of value to promote to.
///
/// - returns: A signal that forwards all terminal events from `self`.
public func promoteValue<U>(_: U.Type = U.self) -> Signal<U, Error> {
return Signal<U, Error> { observer in
return self.observe { event in
switch event {
case .value:
fatalError("Never is impossible to construct")
case let .failed(error):
observer.send(error: error)
case .completed:
observer.sendCompleted()
case .interrupted:
observer.sendInterrupted()
}
}
}
}

/// Promote a signal that does not generate values, as indicated by `Never`, to be
/// a signal of the given type of value.
///
/// - note: The promotion does not result in any value being generated.
///
/// - parameters:
/// - _ The type of value to promote to.
///
/// - returns: A signal that forwards all terminal events from `self`.
public func promoteValue(_: Value.Type = Value.self) -> Signal<Value, Error> {
return self
}
}

extension Signal where Value == Bool {
/// Create a signal that computes a logical NOT in the latest values of `self`.
///
Expand Down
28 changes: 28 additions & 0 deletions Sources/SignalProducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,34 @@ extension SignalProducer where Error == AnyError {
}
}

extension SignalProducer where Value == Never {
/// Promote a signal that does not generate values, as indicated by `Never`, to be
/// a signal of the given type of value.
///
/// - note: The promotion does not result in any value being generated.
///
/// - parameters:
/// - _ The type of value to promote to.
///
/// - returns: A signal that forwards all terminal events from `self`.
public func promoteValue<U>(_: U.Type = U.self) -> SignalProducer<U, Error> {
return lift { $0.promoteValue(U.self) }
}

/// Promote a signal that does not generate values, as indicated by `Never`, to be
/// a signal of the given type of value.
///
/// - note: The promotion does not result in any value being generated.
///
/// - parameters:
/// - _ The type of value to promote to.
///
/// - returns: A signal that forwards all terminal events from `self`.
public func promoteValue(_: Value.Type = Value.self) -> SignalProducer<Value, Error> {
return self
}
}

extension SignalProducer where Value: Equatable {
/// Forward only values from `self` that are not equal to its immediately preceding
/// value.
Expand Down
10 changes: 10 additions & 0 deletions Tests/ReactiveSwiftTests/SignalSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3105,6 +3105,16 @@ class SignalSpec: QuickSpec {
expect(combined is Signal<(Int, Double, Float, UInt), TestError>) == true
}
}

describe("promoteValue") {
it("should infer the value type from the context") {
let completable = Signal<Never, NoError>.never
let producer: Signal<Int, NoError> = Signal<Int, NoError>.never
.flatMap(.latest) { _ in completable.promoteValue() }

expect((producer as Any) is Signal<Int, NoError>) == true
}
}
}
}

Expand Down