Skip to content

Instantly share code, notes, and snippets.

@achernoprudov
Created April 8, 2021 16:15
Show Gist options
  • Save achernoprudov/cded6d847449cdde94c2984cc6fde87d to your computer and use it in GitHub Desktop.
Save achernoprudov/cded6d847449cdde94c2984cc6fde87d to your computer and use it in GitHub Desktop.
CancellableFuture for Combine framework
import Combine
/// Builds `Publisher` with callable promise that can be cancelled.
/// Used in requests and other cancellable tasks.
///
/// Usage:
/// ```
/// CancellableFuture<String, Error> { promise -> AnyCancellable in
/// let request = buildHttpRequest()
/// request.listenToResponse { result in
/// promise(.success(result))
/// }
///
/// return AnyCancellable {
/// request.cancel()
/// }
/// }
/// ```
///
/// - Parameter callable: function that creates `Output`
/// - Returns: deffered `AnyPublisher`
public class CancellableFuture<Output, Failure: Error>: Publisher {
// MARK: - Aliases
public typealias Promise = (Result<Output, Failure>) -> Void
// MARK: - Static
class CancellableSubscription: Subscription {
// MARK: - Instance variables
private let promiseWrapper: (@escaping Promise) -> AnyCancellable
private let subscriber: AnySubscriber<Output, Failure>
private var canceller: AnyCancellable?
// MARK: - Public
init(
subscriber: AnySubscriber<Output, Failure>,
promiseWrapper: @escaping (@escaping Promise) -> AnyCancellable
) {
self.subscriber = subscriber
self.promiseWrapper = promiseWrapper
}
func request(_ demand: Subscribers.Demand) {
canceller = promiseWrapper { [subscriber] promise in
switch promise {
case .failure(let error):
subscriber.receive(completion: .failure(error))
case .success(let output):
_ = subscriber.receive(output)
subscriber.receive(completion: .finished)
}
}
}
func cancel() {
canceller?.cancel()
}
}
// MARK: - Instance variables
private let promiseWrapper: (@escaping Promise) -> AnyCancellable
// MARK: - Public
public init(_ promiseWrapper: @escaping (@escaping Promise) -> AnyCancellable) {
self.promiseWrapper = promiseWrapper
}
public func receive<S>(
subscriber: S
) where S: Subscriber, Failure == S.Failure, Output == S.Input {
let subscription = CancellableSubscription(
subscriber: AnySubscriber(subscriber),
promiseWrapper: promiseWrapper
)
subscriber.receive(subscription: subscription)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment