Skip to content

Commit

Permalink
Rename SignalProducer.Type.attempt to init(_:).
Browse files Browse the repository at this point in the history
  • Loading branch information
andersio committed Apr 23, 2017
1 parent 48baabe commit 91c09af
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 71 deletions.
12 changes: 11 additions & 1 deletion Sources/Deprecations+Removals.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import Foundation
import Dispatch
import enum Result.NoError
import Result

// MARK: Unavailable methods in ReactiveSwift 2.0.
extension SignalProducerProtocol {
@available(*, unavailable, renamed:"init(_:)")
public static func attempt(_ operation: @escaping () -> Result<Value, Error>) -> SignalProducer<Value, Error> { fatalError() }
}

extension SignalProducerProtocol where Error == AnyError {
@available(*, unavailable, renamed:"init(_:)")
public static func attempt(_ operation: @escaping () throws -> Value) -> SignalProducer<Value, Error> { fatalError() }
}

@available(*, unavailable, renamed:"SignalProducer.timer")
public func timer(interval: DispatchTimeInterval, on scheduler: DateScheduler) -> SignalProducer<Date, NoError> { fatalError() }

Expand Down
93 changes: 39 additions & 54 deletions Sources/SignalProducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,26 @@ public struct SignalProducer<Value, Error: Swift.Error> {
}
}

/// 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:
/// - action: A closure that returns instance of `Result`.
public init(_ action: @escaping () -> Result<Value, Error>) {
self.init { observer, disposable in
action().analysis(ifSuccess: { value in
observer.send(value: value)
observer.sendCompleted()
}, ifFailure: { error in
observer.send(error: error)
})
}
}

/// Creates a producer for a `Signal` that will immediately fail with the
/// given error.
///
Expand Down Expand Up @@ -185,6 +205,25 @@ public struct SignalProducer<Value, Error: Swift.Error> {
}
}

extension SignalProducer where Error == AnyError {
/// Create a `SignalProducer` that will attempt the given failable operation once for
/// each invocation of `start()`.
///
/// Upon success, the started producer will send the resulting value then
/// complete. Upon failure, the started signal will fail with the error that
/// occurred.
///
/// - parameters:
/// - operation: A failable closure.
public init(_ action: @escaping () throws -> Value) {
self.init {
return ReactiveSwift.materialize {
return try action()
}
}
}
}

/// A protocol used to constraint `SignalProducer` operators.
public protocol SignalProducerProtocol {
/// The type of values being sent on the producer
Expand Down Expand Up @@ -1280,60 +1319,6 @@ extension SignalProducer where Error == NoError {
}
}

extension SignalProducer {
/// 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
/// `value` event and then complete or `failed` event if `result`
/// is a `failure`.
public static func attempt(_ operation: @escaping () -> Result<Value, Error>) -> SignalProducer<Value, Error> {
return SignalProducer<Value, Error> { observer, disposable in
operation().analysis(ifSuccess: { value in
observer.send(value: value)
observer.sendCompleted()
}, ifFailure: { error in
observer.send(error: error)
})
}
}
}

// FIXME: SWIFT_COMPILER_ISSUE
//
// One of the `SignalProducer.attempt` overloads is kept in the protocol to
// mitigate an overloading issue. Moving them back to the concrete type would be
// a binary-breaking, source-compatible change.

extension SignalProducerProtocol where Error == AnyError {
/// Create a `SignalProducer` that will attempt the given failable operation once for
/// each invocation of `start()`.
///
/// Upon success, the started producer will send the resulting value then
/// complete. Upon failure, the started signal will fail with the error that
/// occurred.
///
/// - parameters:
/// - operation: A failable closure.
///
/// - returns: A `SignalProducer` that will forward a success as a `value`
/// event and then complete or `failed` event if the closure throws.
public static func attempt(_ operation: @escaping () throws -> Value) -> SignalProducer<Value, AnyError> {
return .attempt {
ReactiveSwift.materialize {
try operation()
}
}
}
}

extension SignalProducer where Error == AnyError {
/// Apply a failable `operation` to values from `self` with successful
/// results forwarded on the returned producer and thrown errors sent as
Expand Down
57 changes: 41 additions & 16 deletions Tests/ReactiveSwiftTests/SignalProducerSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,27 @@ class SignalProducerSpec: QuickSpec {
}
}

describe("init(_ block:)") {
describe("init closure overloading") {
it("should be inferred and overloaded without ambiguity") {
let action: () -> String = { "" }
let throwableAction: () throws -> String = { "" }
let resultAction1: () -> Result<String, NoError> = { .success("") }
let resultAction2: () -> Result<String, AnyError> = { .success("") }
let throwableResultAction: () throws -> Result<String, NoError> = { .success("") }

expect(type(of: SignalProducer(action))) == SignalProducer<String, AnyError>.self
expect(type(of: SignalProducer<String, NoError>(action))) == SignalProducer<String, NoError>.self
expect(type(of: SignalProducer<String, TestError>(action))) == SignalProducer<String, TestError>.self

expect(type(of: SignalProducer(resultAction1))) == SignalProducer<String, NoError>.self
expect(type(of: SignalProducer(resultAction2))) == SignalProducer<String, AnyError>.self

expect(type(of: SignalProducer(throwableAction))) == SignalProducer<String, AnyError>.self
expect(type(of: SignalProducer(throwableResultAction))) == SignalProducer<Result<String, NoError>, AnyError>.self
}
}

describe("init(_:) lazy value") {
it("should not evaluate the supplied closure until started") {
var evaluated: Bool = false
func lazyGetter() -> String {
Expand Down Expand Up @@ -287,7 +307,7 @@ class SignalProducerSpec: QuickSpec {
}
}

describe("SignalProducer.attempt") {
describe("init(_:) lazy result") {
it("should run the operation once per start()") {
var operationRunTimes = 0
let operation: () -> Result<String, NSError> = {
Expand All @@ -296,8 +316,8 @@ class SignalProducerSpec: QuickSpec {
return .success("OperationValue")
}

SignalProducer.attempt(operation).start()
SignalProducer.attempt(operation).start()
SignalProducer(operation).start()
SignalProducer(operation).start()

expect(operationRunTimes) == 2
}
Expand All @@ -308,7 +328,7 @@ class SignalProducerSpec: QuickSpec {
return .success(operationReturnValue)
}

let signalProducer = SignalProducer.attempt(operation)
let signalProducer = SignalProducer(operation)

expect(signalProducer).to(sendValue(operationReturnValue, sendError: nil, complete: true))
}
Expand All @@ -319,20 +339,19 @@ class SignalProducerSpec: QuickSpec {
return .failure(operationError)
}

let signalProducer = SignalProducer.attempt(operation)
let signalProducer = SignalProducer(operation)

expect(signalProducer).to(sendValue(nil, sendError: operationError, complete: false))
}
}

describe("SignalProducer.attempt throws") {
describe("init(_:) throwable lazy value") {
it("should send a successful value then complete") {
let operationReturnValue = "OperationValue"

let signalProducer = SignalProducer
.attempt { () throws -> String in
operationReturnValue
}
let signalProducer = SignalProducer { () throws -> String in
operationReturnValue
}

var error: Error?
signalProducer.startWithFailed {
Expand All @@ -345,10 +364,9 @@ class SignalProducerSpec: QuickSpec {
it("should send the error") {
let operationError = TestError.default

let signalProducer = SignalProducer
.attempt { () throws -> String in
throw operationError
}
let signalProducer = SignalProducer { () throws -> String in
throw operationError
}

var error: TestError?
signalProducer.startWithFailed {
Expand Down Expand Up @@ -2667,6 +2685,13 @@ class SignalProducerSpec: QuickSpec {

// MARK: - Helpers

private func == <T>(left: Expectation<T.Type>, right: Any.Type) {
left.to(NonNilMatcherFunc { actualExpression, failureMessage in
let type: Any.Type = try actualExpression.evaluate()!
return type == right
})
}

extension SignalProducer {
internal static func pipe() -> (SignalProducer, ProducedSignal.Observer) {
let (signal, observer) = ProducedSignal.pipe()
Expand Down Expand Up @@ -2696,6 +2721,6 @@ extension SignalProducer {
}
}

return SignalProducer.attempt(operation)
return SignalProducer(operation)
}
}

0 comments on commit 91c09af

Please sign in to comment.