I have two Action
s with the same input/output/error types, and I'd like to compose them into a single Action
that runs whichever of the two is enabled (with an arbitrary tie-breaker if they both are).
Here's my first, failing, attempt:
let addOrRemove: Action<MyInput, MyOutput, APIRequestError> = Action(enabledIf: add.isEnabled.or(remove.isEnabled)) { input in
if add.isEnabled.value {
return add.apply(input)
} else {
return remove.apply(input)
}
}
This fails because the inner add.apply(input)
can't see that I checked add.isEnabled
, so it wraps an additional ActionError<>
layer around the error type. (This might be legit, as I'm not sure how thread-safe this approach would be, or might be a case of us knowing something the type system doesn't.) The corresponding type error is:
cannot convert return expression of type 'SignalProducer<MyOutput, ActionError<APIRequestError>>' to return type 'SignalProducer<MyOutput, APIRequestError>'
What should I do instead?
Github user @ikesyo provided the following answer on the ReactiveSwift issue I opened to ask the same question:
let producer: SignalProducer<MyOutput, ActionError<APIRequestError>>
if add.isEnabled.value {
producer = add.apply(input)
} else {
producer = remove.apply(input)
}
return producer.flatMapError { error in
switch error {
case .disabled: return .empty
case let .producerFailed(inner): return SignalProducer(error: inner)
}
}
If they show up here I'll happily change the accepted answer to theirs (credit where it belongs).
Warning: If I'm reading this answer correctly, it's not watertight. If add
changes from enabled to disabled between the apply()
and the start()
of the wrapping Action
, we'll get "success" (no values, but .completed
) instead of the .disabled
we should get. That's good enough for my use case, but YMMV.